package org.gdstash.db;

import org.gdstash.file.ARZRecord;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.*;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class DBController {
    public static final String TABLE_NAME = "GD_CONTROLLER";
    public static final String FIELD_ID = "CONTROLLER_ID";
    private static final int ROW_CONTROLLER_ID = 1;
    private static final int ROW_TRIGGER_CHANCE = 2;
    private static final int ROW_TRIGGER_TYPE = 3;
    public static final String TRIGGER_ENEMY_KILL = "OnKill";
    public static final String TRIGGER_ENEMY_HIT = "AttackEnemy";
    public static final String TRIGGER_ENEMY_CRIT = "AttackEnemyCrit";
    public static final String TRIGGER_SELF_HIT = "HitByEnemy";
    public static final String TRIGGER_SELF_CRIT = "HitByCrit";
    public static final String TRIGGER_SELF_HIT_RANGE = "HitByProjectile";
    public static final String TRIGGER_SELF_HIT_MELEE = "HitByMelee";
    public static final String TRIGGER_SELF_BLOCK = "Block";
    public static final String TRIGGER_SELF_LOW_HEALTH = "LowHealth";
    private static ConcurrentHashMap<String, DBController> hashBuffer;
    private String controllerID;
    private int triggerChance;
    private String triggerType;

    public DBController() {
        this.controllerID = null;
        this.triggerChance = 0;
        this.triggerType = null;
    }

    private DBController(final ARZRecord record) {
        this.controllerID = record.getFileName();
        this.triggerChance = record.getTriggerChance();
        this.triggerType = record.getTriggerType();
    }

    static {
        DBController.hashBuffer = new ConcurrentHashMap<String, DBController>();
    }

    public String getControllerID() {
        return this.controllerID;
    }

    public int getTriggerChance() {
        return this.triggerChance;
    }

    public static void clearBuffer() {
        DBController.hashBuffer.clear();
    }

    public static void createTables() throws SQLException {
        final String dropTable = "DROP TABLE GD_CONTROLLER";
        final String createTable = "CREATE TABLE GD_CONTROLLER (CONTROLLER_ID  VARCHAR(256) NOT NULL, TRIGGER_CHANCE    INTEGER    , TRIGGER_TYPE      VARCHAR(32), PRIMARY KEY (CONTROLLER_ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GD_CONTROLLER")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GD_CONTROLLER"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static void delete(final String controllerID) throws SQLException {
        final String deleteEntry = "DELETE FROM GD_CONTROLLER WHERE CONTROLLER_ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
                ps.setString(1, controllerID);
                ps.executeUpdate();
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {controllerID, "GD_CONTROLLER"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_DEL_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        final DBController entry = get(record.getFileName());
        if (entry != null) {
            return;
        }
        final DBController controller = new DBController(record);
        final String insert = "INSERT INTO GD_CONTROLLER VALUES (?,?,?)";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(insert)) {
                ps.setString(1, controller.controllerID);
                ps.setInt(2, controller.triggerChance);
                ps.setString(3, controller.triggerType);
                ps.executeUpdate();
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {record.getFileName(), "GD_CONTROLLER"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_INS_TABLE_BY_ID", args);
                GDMsgLogger.addLowError(msg);
                GDMsgLogger.addLowError(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static DBController get(final String controllerID) {
        DBController controller = DBController.hashBuffer.get(controllerID);
        if (controller == null) {
            controller = getDB(controllerID);
            if (controller != null) {
                DBController.hashBuffer.put(controller.controllerID, controller);
            }
        }
        return controller;
    }

    private static DBController getDB(final String controllerID) {
        DBController controller = null;
        final String command = "SELECT * FROM GD_CONTROLLER WHERE CONTROLLER_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, controllerID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBController> list = wrap(rs);
                if (list.isEmpty()) {
                    controller = null;
                } else {
                    controller = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {controllerID, "GD_CONTROLLER"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return controller;
    }

    private static List<DBController> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBController> list = new LinkedList<DBController>();
        while (rs.next()) {
            final DBController controller = new DBController();
            controller.controllerID = rs.getString(1);
            controller.triggerChance = rs.getInt(2);
            controller.triggerType = rs.getString(3);
            list.add(controller);
        }
        return list;
    }

    public static String getTriggerDescription(final String triggerType, final int triggerChance) {
        if (triggerType == null) {
            return null;
        }
        if (triggerChance == 0) {
            return null;
        }
        String tag = null;
        if (triggerType.equals("OnKill")) {
            tag = "SKILL_TRIGGER_ENEMY_KILL";
        }
        if (triggerType.equals("AttackEnemy")) {
            tag = "SKILL_TRIGGER_ENEMY_HIT";
        }
        if (triggerType.equals("AttackEnemyCrit")) {
            tag = "SKILL_TRIGGER_ENEMY_CRIT";
        }
        if (triggerType.equals("HitByEnemy")) {
            tag = "SKILL_TRIGGER_SELF_HIT";
        }
        if (triggerType.equals("HitByCrit")) {
            tag = "SKILL_TRIGGER_SELF_CRIT";
        }
        if (triggerType.equals("HitByProjectile")) {
            tag = "SKILL_TRIGGER_SELF_HIT_RANGE";
        }
        if (triggerType.equals("HitByMelee")) {
            tag = "SKILL_TRIGGER_SELF_HIT_MELEE";
        }
        if (triggerType.equals("Block")) {
            tag = "SKILL_TRIGGER_SELF_BLOCK";
        }
        if (triggerType.equals("LowHealth")) {
            tag = "SKILL_TRIGGER_SELF_LOW_HEALTH";
        }
        if (tag == null) {
            return null;
        }
        String text = null;
        final Object[] iArgs = {triggerChance};
        text = GDMsgFormatter.format(GDMsgFormatter.rbGD, tag, iArgs);
        return text;
    }

    public String getTriggerType() {
        return this.triggerType;
    }
}
